package com.squeekeek.util.japandict;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.GZIPInputStream;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class DictUpdatePanel extends JPanel {
	
	public DictUpdatePanel() {
		JLabel updatelbl;
		JButton updatebtn;
		JButton exitbtn;
		JProgressBar loadbar;
		
		GroupLayout layout;
		ActionListener myalistener;
		
		updatelbl = new JLabel("Update Progress");
		updatebtn = new JButton("Update");
		exitbtn = new JButton("Exit");
		
		loadbar = new JProgressBar(0, 100);
		loadbar.setValue(0);
		loadbar.setStringPainted(true);
		loadbar.setString("Ready");
		
		myalistener = new EvtListener(loadbar, updatebtn);
		updatebtn.setActionCommand("update");
		exitbtn.setActionCommand("exit");
		
		updatebtn.addActionListener(myalistener);
		exitbtn.addActionListener(myalistener);
		
		layout = new GroupLayout(this);
		this.setLayout(layout);
		
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);
		
		layout.setHorizontalGroup(layout.createSequentialGroup()
			.addComponent(updatelbl)
			.addComponent(loadbar)
			.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addComponent(updatebtn)
				.addComponent(exitbtn)
			)
		);
		
		layout.setVerticalGroup(layout.createSequentialGroup()
			.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
				.addComponent(updatelbl)
				.addComponent(loadbar)
				.addComponent(updatebtn)
			)
			.addComponent(exitbtn)
		);
	}
}

/**
 * Task that downloads the dictionary
 * 
 * @author Carl
 *
 */
class DownloadTask extends SwingWorker <Void, Void> {
	public DownloadTask() {
	}
	
	@Override
	public Void doInBackground() {
		int bytesread;
		int filesize;
		int dlprogress = 0;
		int totalbytesread = 0;
		byte[] bytebuf = new byte[153600];
		URLConnection dicturlh;
		BufferedReader updatereadh;
		BufferedReader logreadh;
		InputStream datainh;
		FileOutputStream dataouth;
		
		try {
			this.setProgress(0);
			
			// Check if the dictionary is in the latest edition
			updatereadh = new BufferedReader(new InputStreamReader(new URL("http://ftp.monash.edu.au/pub/nihongo/edicthdr.txt").openStream()));
			
			try {
				logreadh = new BufferedReader(new InputStreamReader(new FileInputStream("update.log")));
			}
			catch(IOException ioee) {
				logreadh = null;
			}
				
			if(!new File("update.log").exists() || !updatereadh.readLine().equals(logreadh.readLine())) {
				// Download update log
				datainh = new URL("http://ftp.monash.edu.au/pub/nihongo/edicthdr.txt").openConnection().getInputStream();
				dataouth = new FileOutputStream("update.log");
				
				while((bytesread = datainh.read(bytebuf)) > 0) {
					dataouth.write(bytebuf, 0, bytesread);
					bytebuf = new byte[153600];
				}
				
				datainh.close();
				dataouth.close();
				
				// Download dictionary
				dicturlh = new URL("http://ftp.monash.edu.au/pub/nihongo/edict2.gz").openConnection();
				datainh = dicturlh.getInputStream();
				dataouth = new FileOutputStream("edict2.gz");
				
				filesize = dicturlh.getContentLength();
				
				while((bytesread = datainh.read(bytebuf)) > 0) {
					dataouth.write(bytebuf, 0, bytesread);
					bytebuf = new byte[153600];
					totalbytesread += bytesread;
					
					dlprogress = (totalbytesread * 100) / filesize;
					this.setProgress(Math.min(dlprogress, 100));
				}
				
				datainh.close();
				dataouth.close();
			}
			else {
				JOptionPane.showMessageDialog(null, "The dictionary is in its latest version.");
			}
			
			updatereadh.close();
			logreadh.close();
		}
		catch(IOException ioe) {
			JOptionPane.showMessageDialog(null, "IOException at class DownloadTask:\n" + ioe.getMessage());
		}
		
		return null;
	}

	public void done() {
	}
}

/**
 * Task that decompress the downloaded dictionary
 * 
 * @author Carl
 *
 */
class DecompressTask extends SwingWorker <Void, Void> {
	public DecompressTask() {
	}
	
	@Override
	protected Void doInBackground() {
		int bytesread;
		int filesize;
		int dfltprogress = 0;
		int totalbytesread = 0;
		byte[] bytebuf = new byte[153600];
		InputStream datainh;
		FileOutputStream dataouth;
		
		try {
			if(new File("edict2.gz").exists()) {
				datainh = new BufferedInputStream(new GZIPInputStream(new FileInputStream("edict2.gz")));
				dataouth = new FileOutputStream("edict2");
				
				filesize = (int) new File("edict2.gz").length();
				
				while((bytesread = datainh.read(bytebuf)) > 0) {
					dataouth.write(bytebuf, 0, bytesread);
					bytebuf = new byte[153600];
					totalbytesread += bytesread;
					
					dfltprogress = (totalbytesread * 100) / filesize;
					this.setProgress(Math.min(dfltprogress, 100));
				}
				
				datainh.close();
				dataouth.close();
			}
		}
		catch(IOException ioe) {
			JOptionPane.showMessageDialog(null, "IOException at class DecompressTask:\n" + ioe.getMessage());
		}
		
		return null;
	}
	
	public void done() {
		new File("edict2.gz").delete();
	}
}

/**
 * Event listener class for the class DictGetPanel
 * 
 * @author Carl
 *
 */
class EvtListener implements ActionListener, PropertyChangeListener {
	private JButton myupdatebtn;
	private JProgressBar myprogressbar;
	
	public EvtListener(JProgressBar mypbar, JButton myubtn) {
		this.myprogressbar = mypbar;
		this.myupdatebtn = myubtn;
	}
	
	@Override
	public void propertyChange(PropertyChangeEvent pce) {
		if("progress" == pce.getPropertyName()) {
			this.myprogressbar.setValue((Integer) pce.getNewValue());
			
			if(pce.getSource() instanceof DownloadTask) {
				this.myprogressbar.setString("Downloading " + this.myprogressbar.getValue() + "%");
			}
			else if(pce.getSource() instanceof DecompressTask) {
				this.myprogressbar.setString("Decompressing " + this.myprogressbar.getValue() + "%");
			}
		}
	}

	@Override
	public void actionPerformed(ActionEvent ae) {
		switch(ae.getActionCommand()) {
		case "update":
			ExecutorService myexec;
			DownloadTask dltask;
			DecompressTask dflttask;
			DoneProgress donetask;
			
			this.myprogressbar.setString("Downloading 0%");
			this.myupdatebtn.setEnabled(false);
			
			dltask = new DownloadTask();
			dflttask = new DecompressTask();
			donetask = new DoneProgress(this.myprogressbar, this.myupdatebtn);
			dltask.addPropertyChangeListener(this);
			dflttask.addPropertyChangeListener(this);
			
			// Execute downloading and decompressing one at a time
			myexec = Executors.newSingleThreadExecutor();
			myexec.submit(dltask);
			myexec.submit(dflttask);
			myexec.submit(donetask);
				
			break;
		case "exit":
			System.exit(0);
			break;
		}
	}
	
	class DoneProgress implements Runnable {
		private JProgressBar myprogressbar;
		private JButton myupdatebtn;
		
		public DoneProgress(JProgressBar mypbar, JButton myubtn) {
			this.myprogressbar = mypbar;
			this.myupdatebtn = myubtn;
		}
		
		public void run() {
			this.myprogressbar.setString("Done!");
			this.myupdatebtn.setEnabled(true);
		}
	}
	
}